package com.sec.android.app.myfiles.activity;

import android.Manifest;
import android.app.AlarmManager;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.Instrumentation;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.Window;

import com.sec.android.app.myfiles.R;
import com.sec.android.app.myfiles.decorator.gridview.GridViewDecorator;
import com.sec.android.app.myfiles.dialog.PermissionDialogFragment;
import com.sec.android.app.myfiles.facade.MyFilesFacade;
import com.sec.android.app.myfiles.facade.cmd.DeleteRecordCmd;
import com.sec.android.app.myfiles.feature.CloudMgr;
import com.sec.android.app.myfiles.feature.LayoutMgr;
import com.sec.android.app.myfiles.feature.MouseKeyboardMgr;
import com.sec.android.app.myfiles.feature.MultiWindowMgr;
import com.sec.android.app.myfiles.feature.SmartTipMgr;
import com.sec.android.app.myfiles.fragment.AbsMyFilesFragment;
import com.sec.android.app.myfiles.fragment.filelist.FileListActionMenu;
import com.sec.android.app.myfiles.fragment.search.SearchInputView;
import com.sec.android.app.myfiles.info.AppConstants;
import com.sec.android.app.myfiles.info.AppFeatures;
import com.sec.android.app.myfiles.launch.LaunchMgr;
import com.sec.android.app.myfiles.listener.GmsLaunchedReceiver;
import com.sec.android.app.myfiles.listener.ListenerMgr;
import com.sec.android.app.myfiles.listener.ListenerMgr.LifeCycle;
import com.sec.android.app.myfiles.log.Log;
import com.sec.android.app.myfiles.log.Log.SluggishType;
import com.sec.android.app.myfiles.log.SamsungAnalyticsLog;
import com.sec.android.app.myfiles.module.abstraction.FileRecord;
import com.sec.android.app.myfiles.module.shortcut.ManageShortcutListFragment;
import com.sec.android.app.myfiles.navigation.NavigationChangedObserver.NavigationChangedListener;
import com.sec.android.app.myfiles.navigation.NavigationInfo;
import com.sec.android.app.myfiles.navigation.NavigationManager;
import com.sec.android.app.myfiles.provider.PrivateMyFilesProvider;
import com.sec.android.app.myfiles.thumbnail.ThumbnailMgr;
import com.sec.android.app.myfiles.util.DrawableMgr;
import com.sec.android.app.myfiles.util.FolderAppIconMgr;
import com.sec.android.app.myfiles.util.NotificationUtils;
import com.sec.android.app.myfiles.util.PreferenceUtils;
import com.sec.android.app.myfiles.util.StorageMonitor;
import com.sec.android.app.myfiles.util.UiUtils;
import com.sec.android.app.myfiles.widget.listview.SelectionManager;

/**
 * <pre>
 * Activity class for File list.
 * It manage NavigationMgr and Layout.
 * </pre>
 *
 * @author jae.bae
 */
public class FileListActivity extends AbsMyFilesActivity implements NavigationChangedListener, ActivityCompat.OnRequestPermissionsResultCallback {
	
	private static final String TAG = FileListActivity.class.getSimpleName();
	
    protected NavigationManager mNavigationManager;
    private FileListActionMenu mActionMenu;
    private AbsMyFilesFragment mFragment;
    private SearchInputView mSearchView;
    private LayoutMgr mLayoutMgr;
    private ListenerMgr mListenerMgr;
    private int mDexModeDecorCaptionViewHeight;
    private Configuration mPrevConfig;
    private static boolean sChangedUiMode = false;

    private static final int ENTRANCE_PERMISSIONS = 0;
    private boolean mAlwaysDenied;
    private boolean mPrevDesktopModeEnabled;
    private boolean mDesktopModeChanged;

    private SelectionManager mSelectionManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(this, "onCreate");
        Log.sluggish(SluggishType.OnCreate);
        SamsungAnalyticsLog.setAutoLogging();
        createDesktopModeMgr();
        StorageMonitor.updateSubSystemStorageState(getApplicationContext());

        mLayoutMgr = new LayoutMgr(this);
        mLayoutMgr.onCreate();

        mListenerMgr = new ListenerMgr();
        mListenerMgr.addListener(new GmsLaunchedReceiver(this, LifeCycle.CREATE, LifeCycle.DESTROY));
        mListenerMgr.notifyCreate();

        int processId = getProcessId();
        mNavigationManager = NavigationManager.getInstance(processId);
        mNavigationManager.registerNavigationChangedListener(this);

        mSelectionManager = SelectionManager.getInstance(processId);
        mNavigationManager.registerNavigationChangedListener(mSelectionManager);

        UiUtils.clearCacheString();
        onHandleStart();
        Intent intent = getIntent();
        if (AppConstants.Action.RUN_OPTIMIZE_STORAGE.equals(intent.getAction())) {
            PreferenceUtils.setMoreOptionBadgeIcon(getApplicationContext(), false);
        } else if (PreferenceUtils.getPackageDataCleared(getApplicationContext())) {
            if (!NotificationUtils.isPreviousAlarmExist(getApplicationContext())) {
                Log.d(this, "onCreate() ] Previous alarm isn't existed & Package data cleared. So register new alarm.");
                NotificationUtils.releaseAlarm(getApplicationContext(), AppConstants.ACTION_CHECK_FREE_SPACE);
                NotificationUtils.registerNewAlarm(getApplicationContext(), AppConstants.ACTION_CHECK_FREE_SPACE,
                        AlarmManager.INTERVAL_HOUR, AlarmManager.INTERVAL_HOUR);
            }
        }
        PreferenceUtils.setPackageDataCleared(getApplicationContext(), false);

        createActionMenu();

        CloudMgr.keepCloudServiceAlive(this, true);
    }

    protected void onHandleStart() {
        if (isPermissionValid()) {
            int processId = getProcessId();
            Log.d(this, "onHandleStart() ] Permission is valid. So start.");
            LaunchMgr.getInstance(processId).handleStart(processId, this, getIntent());
        } else {
            if (mAlwaysDenied) {
                Log.d(this, "onHandleStart() ] runApplicationSettings!");
                runApplicationSettings(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE});
            } else {
                Log.d(this, "onHandleStart() ] Call requestPermissions()");
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, FileListActivity.ENTRANCE_PERMISSIONS);
            }
        }
    }

    private void runApplicationSettings(String[] permission) {
        PermissionDialogFragment dialog = new PermissionDialogFragment();
        PermissionDialogFragment.setArguments(permission);
        dialog.show(getFragmentManager(), "PermissionDialog_" + permission.toString());
    }

    private boolean isPermissionValid() {
        boolean bRet = true;
        mAlwaysDenied = false;
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            if (!PreferenceUtils.isFirstEntry(this) &&
                    !ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                mAlwaysDenied = true;
            }
            bRet = false;
        }
        PreferenceUtils.setFirstEntry(this, false);
        Log.d(this, "isPermissionValid() ] return " + bRet);
        return bRet;
    }

    private void createActionMenu() {
        mActionMenu = new FileListActionMenu(this, null, null);
    }

    public FileListActionMenu getActionMenu() {
        return mActionMenu;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        Log.d(this, "onCreateOptionsMenu");

        MenuInflater inflater = getMenuInflater();
        if (mActionMenu != null) {
            mActionMenu.onCreateOptionsMenu(null, menu, inflater, AbsMyFilesFragment.ActionModeType.NORMAL);
        }

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        if (mActionMenu != null) {
            mFragment = getCurFragment();
            if (mFragment != null) {
                mActionMenu.onPrepareOptionsMenu(menu);
            }
        }
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        int processId = getProcessId();
        LaunchMgr.getInstance(processId).handleStart(processId, this, intent);

        if (AppConstants.Action.RUN_OPTIMIZE_STORAGE.equals(intent.getAction())) {
            PreferenceUtils.setMoreOptionBadgeIcon(getApplicationContext(), false);
        } else if (intent.getBooleanExtra(AppConstants.MyFilesExtras.EXTRA_START_BIXBY, false)) {
            MyFilesFacade.goHome(processId, null, this);
            Log.d(this, "onNewIntent() Start MyFiles by Bixby");
        }
    }

    @Override
    protected void onDestroy() {
        Log.d(this, "onDestroy()");
        super.onDestroy();
        int processId = getProcessId();
        mSelectionManager.clearInstance(processId);
        mNavigationManager.unregisterNavigationChangedListener(mSelectionManager);
        mNavigationManager.unregisterNavigationChangedListener(this);
        NavigationManager.clearInstance(processId);
        MultiWindowMgr.clearInstance(processId);
        SearchInputView.clearInstance(processId);
        SmartTipMgr.clearInstance();
        mLayoutMgr.onDestroy();
        mLayoutMgr = null;
        mListenerMgr.notifyDestroy();
        mListenerMgr = null;
        CloudMgr.keepCloudServiceAlive(this, false);
    }

    public boolean getDesktopModeChanged() {
        return AppFeatures.isSupportDesktopModeListener() ? mDesktopModeChanged : sChangedUiMode;
    }

    private boolean isDesktopModeChanged() {
        return false;
    }

    private void checkDiffConfig(Configuration newConfig) {
        int diff = newConfig.diff(mPrevConfig);

        sChangedUiMode = ((diff & ActivityInfo.CONFIG_UI_MODE) != 0);
        Log.d(this, "CONFIG_UI_MODE has been changed - " + sChangedUiMode);
        mPrevConfig = new Configuration(newConfig);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        Log.a(this, "onConfigurationChanged" + newConfig.getLayoutDirection());
        super.onConfigurationChanged(newConfig);

        setDensityDpi(newConfig.densityDpi);
        if (AppFeatures.isSupportDesktopModeListener()) {
            if (getDesktopModeChanged() && !mIsDexModeChanging) {
                recreateActivity();
            }
        } else {
            checkDiffConfig(newConfig);
            if (sChangedUiMode) {
                DrawableMgr.clearIconDrawables();
                FolderAppIconMgr.clearMergedIconDrawables();
                recreate();
            }
        }

        if (mSearchView != null) {
            mSearchView.refreshSearchViewWidth(newConfig);
        }

        mLayoutMgr.onConfigurationChanged(newConfig);
    }

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
        if (level < TRIM_MEMORY_RUNNING_CRITICAL) {
            ThumbnailMgr.getInstance(this).clearCache();
        }
    }

    @Override
    public void onBackPressed() {
        Log.a(this, "back_key");
        if (GridViewDecorator.getIsPinchWorking()) {
            return;
        }

        NavigationInfo navigationInfo;
        if (!preventLeave()) {
            navigationInfo = mNavigationManager.leave(this);
        } else {
            navigationInfo = null;
        }

        if (navigationInfo == null) {
            super.onBackPressed();
        }
    }

    /**
     * Prevent close app by back key in Pin-Window mode
     */
    private boolean preventLeave() {
        boolean bRet = false;

        if (mNavigationManager.getDepth() <= 1) {
            bRet = isPinWindow() || (isSearchViewExpanded() && !AppFeatures.isTabletUIMode());
        }

        return bRet;
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.sluggish(SluggishType.OnResume);

        if (!AppFeatures.isSupportDesktopModeListener()) {
            mPrevConfig = new Configuration(getResources().getConfiguration());
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mActionMenu != null) {
            mActionMenu.onPause();
        }
    }

    /**
     * <pre>
     * It triggered by enter() or leave() in navigationManager.
     * Refresh DB and call LayoutMgr
     * </pre>
     */
    @Override
    public void onNavigationChanged(NavigationInfo preInfo, NavigationInfo curInfo) {
        StringBuilder msg = new StringBuilder("onNavigationChanged");
        msg.append(" [");
        if (preInfo != null && preInfo.getCurRecord() != null) {
            msg.append(preInfo.getCurRecord());
        }
        msg.append("] -> [");
        if (curInfo != null && curInfo.getCurRecord() != null) {
            msg.append(curInfo.getCurRecord());
        }
        msg.append("]");

        Log.d(TAG, msg.toString());

        if (curInfo != null) {
            if (!curInfo.isNavigationModeChanged(preInfo)) {
				if (curInfo.getCurRecord() != null) {
				PrivateMyFilesProvider.stopRefreshDb(getApplicationContext(), curInfo.getCurRecord().getStorageType());
                PrivateMyFilesProvider.refreshDb(getApplicationContext(), curInfo.getCurRecord());
				}
            }

            mLayoutMgr.onNavigationChanged(preInfo, curInfo);

            if (mSearchView != null) {
                mSearchView.onNavigationChanged(preInfo, curInfo);
            }

            AbsMyFilesFragment curFragment = (preInfo != null) ? preInfo.getCurFragment() : null;
            if (needInvalidateMenu(preInfo, curFragment)) {
                mFragment = curInfo.getCurFragment();
                mActionMenu.setInfo(mFragment, curInfo);
                invalidateOptionsMenu();
            }
        }
    }

    private boolean needInvalidateMenu(NavigationInfo preInfo, AbsMyFilesFragment curFragment) {
        boolean bRet = false;
        if (mActionMenu.getFragment() == null || (curFragment != null && curFragment.isSelectActionMode())) {
            bRet = true;
        } else if (preInfo != null) {
            if (preInfo.refreshActionMenu()) {
                bRet = true;
            } else if ((preInfo.getNavigationMode() == NavigationInfo.NavigationMode.Normal && preInfo.getStorageType() == FileRecord.StorageType.Search)
                    && (mSearchView != null && !mSearchView.isSipVisible() && AppFeatures.isTabletUIMode())) {
                bRet = true;
            }
        }

        return bRet;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                SamsungAnalyticsLog.sendLog(getProcessId(), SamsungAnalyticsLog.Event.NAVIGATE_UP, null);
                if (!mNavigationManager.goUp(this)) {
                    super.onBackPressed();
                }
                break;
            default:
                mActionMenu.onOptionsItemSelected(item);
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        mFragment = getCurFragment();
        if (mFragment != null) {
            mFragment.onActivityResult(requestCode, resultCode, data);
        }

        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        Log.d(this, "onKeyUp - " + keyCode);
        if (keyCode == KeyEvent.KEYCODE_SEARCH) {
            if (mSearchView != null) {
                mSearchView.dispatchSearchKey();
            }
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
            if (mFragment != null && !(mFragment instanceof ManageShortcutListFragment)) {
                MyFilesFacade.deleteRecord(getProcessId(), getApplicationContext(), mFragment, mFragment.getSelectedFile(),
                        DeleteRecordCmd.OperationProgress.SHOW_CONFIRM_POPUP);
            }
            return true;
        }

        MouseKeyboardMgr.setShftPress(false);
        MouseKeyboardMgr.setCtrlPress(false);
        return super.onKeyUp(keyCode, event);
    }

    @Override
    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
        final int filteredMetaState = event.getMetaState() & ~KeyEvent.META_CTRL_MASK;
        if (KeyEvent.metaStateHasNoModifiers(filteredMetaState)) {
            if (keyCode == KeyEvent.KEYCODE_F) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            new Instrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_SEARCH);
                        } catch (Exception ex) {
                            Log.d(this, ex.getMessage());
                        }
                    }
                }).start();
            }
        }
        return super.onKeyShortcut(keyCode, event);
    }

    @Override
    public boolean dispatchGenericMotionEvent(MotionEvent event) {
        if (AppFeatures.isKnoxDesktopMode()) {
            if (MouseKeyboardMgr.isCtrlPressed() && event.getAction() == MotionEvent.ACTION_SCROLL) {
                NavigationInfo curInfo = mNavigationManager.getCurInfo();
                if (curInfo != null) {
                    MouseKeyboardMgr.onCtrlMouseWheel(event, curInfo.getCurFragment(), curInfo.getCurRecord());
                }
                return true;
            }
            if (MouseKeyboardMgr.isCtrlPressed() && event.getAction() == MotionEvent.ACTION_BUTTON_RELEASE) {
                MouseKeyboardMgr.onCtrlMouseClick(mFragment, mFragment.getListView(), -1);
                return true;
            }
        }
        return super.dispatchGenericMotionEvent(event);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        boolean granted = true;

        Log.d(this, "onRequestPermissionsResult() ] requestCode = " + requestCode);
        if (requestCode == ENTRANCE_PERMISSIONS) {
            if (permissions.length == 0 || grantResults.length == 0) {
                Log.d(this, "onRequestPermissionsResult() ] Abnormal case. All tasks related to MyFiles are terminated now.");
                finishAffinity();
                return;
            }

            for (int grantResult : grantResults) {
                if (grantResult == PackageManager.PERMISSION_DENIED) {
                    granted = false;
                    break;
                }
            }
            if (granted) {
                LaunchMgr.getInstance(getProcessId()).handleStart(getProcessId(), this, getIntent());
            } else {
                Log.d(this, "onRequestPermissionsResult() ] The permission is denied. So finish MyFiles.");
                finish();
            }
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (isSearchViewExpanded()) { //Dispatch touch event only when SearchView expanded
            if (event.getAction() == MotionEvent.ACTION_DOWN && event.getY() > getActionBarHeight() + getStatusBarHeight()) {
                // Hide a SIP If the user touches the screen out of SIP except SearchView(Actionbar)
                mSearchView.updateSip(false, AppFeatures.SIP_APPEARANCE_DELAY);
            }
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    protected void onUserLeaveHint() {
        super.onUserLeaveHint();
        if (mSearchView != null && mSearchView.isSipVisible()) {
            mSearchView.updateSip(false, 0);
        }
    }

    private int getActionBarHeight() {
        int ret = 0;
        TypedValue tv = new TypedValue();
        if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
            ret = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
        }
        return ret;
    }

    private int getStatusBarHeight() {
        if (AppFeatures.isKnoxDesktopMode()) {
            return mDexModeDecorCaptionViewHeight;
        } else if (UiUtils.isFullScreen(this)) {
            return 0;
        }
        Rect rectangle = new Rect();
        Window window = getWindow();
        window.getDecorView().getWindowVisibleDisplayFrame(rectangle);
        int statusBarHeight = rectangle.top;
        int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
        return contentViewTop - statusBarHeight;
    }

    public void setSearchMenu(Menu menu) {
        MenuItem searchMenu = menu.findItem(R.id.menu_search);
        NavigationInfo curInfo = mNavigationManager.getCurInfo();

        if (searchMenu != null && curInfo != null) {
            mSearchView = SearchInputView.getInstance(this, menu, curInfo);
        }
    }

    public SearchInputView getSearchView() {
        return mSearchView;
    }

    public boolean isSearchViewExpanded() {
        boolean bRet = false;
        mFragment = getCurFragment();
        if (mSearchView != null && mFragment != null) {
            bRet = mSearchView.isSearchMenuExpanded() && !mFragment.isSelectActionMode();
        }

        return bRet;
    }

    private AbsMyFilesFragment getCurFragment() {
        AbsMyFilesFragment curFragment = null;
        NavigationInfo curInfo = mNavigationManager.getCurInfo();
        if (curInfo != null) {
            curFragment = curInfo.getCurFragment();
        }

        return curFragment;
    }

    public static AbsMyFilesFragment searchForCurFragment(int processId) {
        AbsMyFilesFragment ret = null;
        AbsMyFilesActivity activity = getMyFilesActivity(processId);
        if (activity instanceof FileListActivity) {
            FragmentManager fm = activity.getFragmentManager();
            if (fm != null) {
                Fragment fragment = fm.findFragmentById(R.id.detail_list_container);
                if (fragment instanceof AbsMyFilesFragment) {
                    ret = (AbsMyFilesFragment) fragment;
                }
            }
        }
        return ret;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.d(this, "onKeyDown - " + keyCode);
        if (event.isShiftPressed()) {
            MouseKeyboardMgr.setShftPress(true);
        }
        if (event.isCtrlPressed()) {
            MouseKeyboardMgr.setCtrlPress(true);
        }
        if (isBackSpacePressed(keyCode)) {
            mNavigationManager.leave(this);
        }
        return super.onKeyDown(keyCode, event);
    }

    private boolean isBackSpacePressed(int keyCode) {
        boolean isSipVisible = false;
        if (mSearchView != null) {
            isSipVisible = mSearchView.isSipVisible();
        }
        return AppFeatures.isKnoxDesktopMode() && keyCode == KeyEvent.KEYCODE_DEL && !isSipVisible;
    }

    public LayoutMgr getLayoutMgr() {
        return mLayoutMgr;
    }

    @Override
    public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
        super.onMultiWindowModeChanged(isInMultiWindowMode);
        // To do something
    }

    private void createDesktopModeMgr() {
    }

    private boolean mIsDexModeChanging;


    private void recreateActivity() {
        Log.d(this, "recreateActivity()");
        DrawableMgr.clearIconDrawables();
        FolderAppIconMgr.clearMergedIconDrawables();
        recreate();
    }
}